﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

using BulletSharp;
using System.Threading;

namespace KinectGame
{
    public enum MenuState
    {
        Visible, Hidden
    }

    public class MenuEventArgs : EventArgs
    {
        public int id;
    }

    public class Menu
    {
        private Vector3 menuObjectStartPos = new Vector3(0, 50, -10);

        private GameObject[] menuObjects = new GameObject[50];
        public GameObject[] MenuObjects
        {
            get { return menuObjects; }
        }

        private List<MenuElement> menuElements = new List<MenuElement>();
        public List<MenuElement> MenuElements
        {
            get { return menuElements; }
        }

        private MenuElement choicedElement;

        private GameObject ground;

        public GameObject Ground
        {
            get { return ground; }
        }

        private TimeSpan newObjectTimeSpan = new TimeSpan(0, 0, 0, 0, 400);
        private DateTime lastNewObjectTime = DateTime.Now;

        private MenuState state;
        public MenuState State
        {
            get { return state; }
        }

        private int objectCounter = 0;
        private int elementCounter = 0;

        private bool onMove = false;
        public bool Active
        {
            get { return !onMove; }
        }

        private int id;

        public int Id
        {
            get { return id; }
        }

        public event EventHandler OnEscape;

        BoundingBox colB = new BoundingBox(new Vector3(-1, -5, 5), new Vector3(1, 50, 50));

        private float cameraZoom, cameraVerticalAngle;

        public Menu(int id, Vector3 menuObjectStartPosition, float cameraZoom, float cameraVerticalAngle)
        {        

            ground = new GameObject(Game.models["ground"], new Vector3(10, 1, 10), Vector3.Zero, Vector3.Down * 2, 0.0f);
            ground.Activate();

            menuObjectStartPos = menuObjectStartPosition;

            this.id = id;
            this.cameraVerticalAngle = cameraVerticalAngle;
            this.cameraZoom = cameraZoom;
        }

        public void Show()
        {
            NewMenuObjects();  

            Game.World.AddRigidBody(ground.RigidBody);

            elementCounter = menuElements.Count - 1;
            if (menuElements.Count > 0)
            {
                choicedElement = menuElements[elementCounter];
            }
            objectCounter = 0;

            foreach (var menuElement in menuElements)
            {
                Game.World.AddRigidBody(menuElement.ElementRigidBody);
                Game.World.AddConstraint(menuElement.ElementConstraint);
            }

            state = MenuState.Visible;

            Game.Camera.Zoom = cameraZoom;
            Game.Camera.VerticalAngle = cameraVerticalAngle;
            Game.Camera.HorizontalAngle = 0.0f;

            NextChoice();
        }

        public void Hide()
        {
            //lock (Game.World)
            //{
                //Monitor.Wait(Game.World);
                Game.World.RemoveRigidBody(ground.RigidBody);

                foreach (var menuObject in menuObjects)
                {
                    if (menuObject.RigidBody != null)
                    {
                        Game.World.RemoveRigidBody(menuObject.RigidBody);
                    }
                }

                foreach (var menuElement in menuElements)
                {
                    Game.World.RemoveRigidBody(menuElement.ElementRigidBody);
                    Game.World.RemoveConstraint(menuElement.ElementConstraint);
                }
                //Monitor.PulseAll(Game.World);
            //}

            state = MenuState.Hidden;
        }

        public void NextChoice()
        {
            elementCounter++;

            if (elementCounter >= menuElements.Count)
            {
                elementCounter = 0;
            }

            float impPower = 500;
            Vector3 impulse;

            foreach (var e in menuElements)
            {
                e.ElementRigidBody.SetDamping(0.1f, 0.1f);
                e.ElementRigidBody.Activate();
                impulse = e.ElementObject.World.Left;
                impulse.Normalize();
                impulse *= impPower;
                e.ElementRigidBody.ApplyCentralImpulse(impulse);
            }

            choicedElement = menuElements[elementCounter];

            onMove = true;
        }

        public void PreviousChoice()
        {
            elementCounter--;

            if (elementCounter < 0)
            {
                elementCounter = menuElements.Count - 1;
            }

            float impPower = 500;
            Vector3 impulse;

            foreach (var e in menuElements)
            {
                e.ElementRigidBody.SetDamping(0.1f, 0.1f);
                e.ElementRigidBody.Activate();
                impulse = e.ElementObject.World.Right;
                impulse.Normalize();
                impulse *= impPower;
                e.ElementRigidBody.ApplyCentralImpulse(impulse);
            }

            choicedElement = menuElements[elementCounter];

            onMove = true;
        }

        private void NewMenuObjects()
        {
            for (int i = 0; i < menuObjects.Length; i++)
            {
                Random rnd = new Random((int)DateTime.Now.Ticks);

                Vector3 sc = new Vector3((float)(rnd.NextDouble() + 0.5f) * 1.5f);
                Vector3 ro = new Vector3((float)rnd.NextDouble() + 0.5f);

                Vector3 po = new Vector3(menuObjectStartPos.X + ((float)rnd.NextDouble() - 0.5f) * 3, menuObjectStartPos.Y, menuObjectStartPos.Z + ((float)rnd.NextDouble() - 0.5f) * 3);

                switch (i % 4)
                {
                    case 0:
                        {
                            menuObjects[i] = new GameObject(Game.models["box"], sc, ro, po, 1.0f);
                            break;
                        }
                    case 1:
                        {
                            menuObjects[i] = new GameObject(Game.models["sphere"], sc, ro, po, 1.0f);
                            break;
                        }
                    case 2:
                        {
                            menuObjects[i] = new GameObject(Game.models["cylinder"], sc, ro, po, 1.0f);
                            break;
                        }
                    case 3:
                        {
                            menuObjects[i] = new GameObject(Game.models["pyramid"], sc, ro, po, 1.0f);
                            break;
                        }

                    default:
                        {
                            menuObjects[i] = new GameObject(Game.models["box"], sc, ro, po, 1.0f);
                            break;
                        }
                }      

                menuObjects[i].Activate();
                menuObjects[i].Visible = false;
            }
        }

        private void NewMenuObject(int pos)
        {
            Random rnd = new Random();

            if (pos >= 0 && pos < menuObjects.Length)
            {
                if (menuObjects[pos].RigidBody != null)
                {
                    Game.World.RemoveRigidBody(menuObjects[pos].RigidBody);
                }

                Vector3 sc = new Vector3((float)(rnd.NextDouble() + 0.5f) * 1.5f);
                Vector3 ro = new Vector3((float)rnd.NextDouble() + 0.5f);
                Vector3 po = new Vector3(menuObjectStartPos.X + ((float)rnd.NextDouble() - 0.5f) * 3, menuObjectStartPos.Y, menuObjectStartPos.Z + ((float)rnd.NextDouble() - 0.5f) * 3);


                switch (rnd.Next(0, 4))
                {
                    case 0:
                        {
                            menuObjects[pos] = new GameObject(Game.models["box"], sc, ro, po, 1.0f);
                            break;
                        }
                    case 1:
                        {
                            menuObjects[pos] = new GameObject(Game.models["sphere"], sc, ro, po, 1.0f);
                            break;
                        }
                    case 2:
                        {
                            menuObjects[pos] = new GameObject(Game.models["cylinder"], sc, ro, po, 1.0f);
                            break;
                        }
                    case 3:
                        {
                            menuObjects[pos] = new GameObject(Game.models["pyramid"], sc, ro, po, 1.0f);
                            break;
                        }

                    default:
                        {
                            menuObjects[pos] = new GameObject(Game.models["box"], sc, ro, po, 1.0f);
                            break;
                        }
                }      
              
                menuObjects[pos].Activate();
                menuObjects[pos].Visible = false;
            }
        }

        public void Update(GameTime gameTime)
        {      
            if (DateTime.Now - lastNewObjectTime > newObjectTimeSpan)
            {
                if (objectCounter >= menuObjects.Length)
                {
                    NewMenuObject(objectCounter % menuObjects.Length);
                }

                menuObjects[objectCounter % menuObjects.Length].Visible = true;
                Game.World.AddRigidBody(menuObjects[objectCounter % menuObjects.Length].RigidBody);
                lastNewObjectTime = DateTime.Now;

                objectCounter++;
            }

            if (onMove)
            {
                if (choicedElement.ElementRigidBody.LinearVelocity.Length() < 60)
                {
                    if (colB.Contains(choicedElement.ElementRigidBody.WorldTransform.Translation) == ContainmentType.Contains)
                    {
                        float newDamp = 0.7f;

                        foreach (var e in menuElements)
                        {
                            e.ElementRigidBody.SetDamping(newDamp, newDamp);
                        }

                        choicedElement.ElementRigidBody.SetDamping(1000, 1000);
                        choicedElement.InvokeOnChoice();

                        onMove = false;
                    }
                }                
            }
        }

        public int GetActiveElementId()
        {
            return menuElements[elementCounter].Id;
        }

        public MenuElement GetActiveElement()
        {
            return menuElements[elementCounter];
        }

        public void InvokeOnEscape()
        {
            if (OnEscape != null)
            {
                MenuEventArgs e = new MenuEventArgs();
                e.id = this.id;
                OnEscape.Invoke(this, e);
            }
        }
    }
}
